﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Borg.Framework
{
    public class Digit : Control
    {
        private Bitmap[] _bmap;
        private Color _digitBackColor;
        private Rectangle _rect;
        private readonly Point[,] _segs;
        private Size _sz;
        private ContentAlignment _textAlign;

        public Digit()
        {
            var array = new Point[7, 2];

            array[0, 0] = new Point(0, 0);
            array[0, 1] = new Point(6, 0);
            array[1, 0] = new Point(0, 0);
            array[1, 1] = new Point(0, 5);
            array[2, 0] = new Point(6, 0);
            array[2, 1] = new Point(6, 5);
            array[3, 0] = new Point(0, 5);
            array[3, 1] = new Point(6, 5);
            array[4, 0] = new Point(0, 5);
            array[4, 1] = new Point(0, 10);
            array[5, 0] = new Point(6, 5);
            array[5, 1] = new Point(6, 10);
            array[6, 0] = new Point(0, 10);
            array[6, 1] = new Point(6, 10);

            var points = array;
            _segs = points;

            TextChanged      += delegate { Invalidate(); };
            ForeColorChanged += delegate { _bmap = new Bitmap[10]; };
            PaddingChanged   += delegate { MakeRc(); };
            SizeChanged      += delegate { MakeRc(); };
        }

        private Bitmap GetDigit(char ch)
        {
            var digit = ch - 48;

            if (digit < _bmap.GetLowerBound(0) || digit > _bmap.GetUpperBound(0))
                return null;

            if (_bmap[digit] == null)
            {
                var array = new bool[7];
                
                switch (ch)
                {
                    case '0':
                        array[0] = true;
                        array[1] = true;
                        array[2] = true;
                        array[4] = true;
                        array[5] = true;
                        array[6] = true;
                        break;
                    
                    case '1':
                        array[2] = true;
                        array[5] = true;
                        break;
                    
                    case '2':
                        array[0] = true;
                        array[2] = true;
                        array[3] = true;
                        array[4] = true;
                        array[6] = true;
                        break;
                    
                    case '3':
                        array[0] = true;
                        array[2] = true;
                        array[3] = true;
                        array[5] = true;
                        array[6] = true;
                        break;
                    
                    case '4':
                        array[1] = true;
                        array[2] = true;
                        array[3] = true;
                        array[5] = true;
                        break;
                    
                    case '5':
                        array[0] = true;
                        array[1] = true;
                        array[3] = true;
                        array[5] = true;
                        array[6] = true;
                        break;
                    
                    case '6':
                        array[0] = true;
                        array[1] = true;
                        array[3] = true;
                        array[4] = true;
                        array[5] = true;
                        array[6] = true;
                        break;
                    
                    case '7':
                        array[0] = true;
                        array[2] = true;
                        array[5] = true;
                        break;
                    
                    case '8':
                        array[0] = true;
                        array[1] = true;
                        array[2] = true;
                        array[3] = true;
                        array[4] = true;
                        array[5] = true;
                        array[6] = true;
                        break;
                    
                    case '9':
                        array[0] = true;
                        array[1] = true;
                        array[2] = true;
                        array[3] = true;
                        array[5] = true;
                        array[6] = true;
                        break;
                }

                using (var pen = new Pen(ForeColor, 1f))
                {
                    _bmap[digit] = new Bitmap(_sz.Width, _sz.Height);
                    var graphics = Graphics.FromImage(_bmap[digit]);

                    for (var i = 0; i < _segs.GetLength(0); ++i)
                        if (array[i])
                            graphics.DrawLine(pen, _segs[i, 0], _segs[i, 1]);
                }
            }

            return _bmap[digit];
        }

        private void MakeRc()
        {
            _rect = ClientRectangle;
            _rect.Offset(Padding.Left, Padding.Top);

            _rect.Width -= Padding.Horizontal;
            _rect.Height -= Padding.Vertical;

            _sz = new Size(Math.Max(7, _rect.Height * 7 / 11), Math.Max(11, _rect.Height));
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            var rect = new Rectangle(0, _rect.Top, _sz.Width, _sz.Height);
            var area = Text.Length * (_sz.Width + 1) - 1;
            
            switch (_textAlign)
            {
                case ContentAlignment.BottomCenter:
                case ContentAlignment.TopCenter:
                case ContentAlignment.MiddleCenter:
                    rect.X = _rect.Left + (_rect.Width - area) / 2;
                    break;
                
                case ContentAlignment.BottomRight:
                case ContentAlignment.MiddleRight:
                case ContentAlignment.TopRight:
                    rect.X = _rect.Right - area;
                    break;
                
                case ContentAlignment.BottomLeft:
                case ContentAlignment.TopLeft:
                case ContentAlignment.MiddleLeft:
                    rect.X = _rect.Left;
                    break;
            }

            using (Brush brush = new SolidBrush(_digitBackColor))
            {
                int x = _sz.Width + 1;

                foreach (var ch in Text)
                {
                    e.Graphics.FillRectangle(brush, rect);
                    var digit = GetDigit(ch);

                    if (digit != null)
                        e.Graphics.DrawImage(digit, rect);

                    rect.Offset(x, 0);
                }
            }
        }

        [Category("Appearance")]
        [DisplayName("DigitBackColor")]
        [Description("Background color of digits")]
        public Color DigitBackColor
        {
            get { return _digitBackColor; }
            set
            {
                _digitBackColor = value;
                Invalidate();
            }
        }

        [Description("Determine the position of the digits")]
        [Category("Appearance")]
        [DisplayName("TextAlign")]
        public ContentAlignment TextAlign
        {
            get { return _textAlign; }
            set
            {
                _textAlign = value;
                Invalidate();
            }
        }
    }
}

